using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    //定义Web宿主
    public interface IWebHost:IDisposable
    {
        /// <summary>
        /// 
        /// </summary>
        IFeatureCollection ServerFeatures { get; }

        /// <summary>
        /// 服务容器
        /// </summary>
        IServiceProvider Services { get; }

        /// <summary>
        /// 开始监听端口
        /// </summary>
        void Start();

        /// <summary>
        ///启动主机
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task StartAsync(CancellationToken cancellationToken = default(CancellationToken));

        /// <summary>
        /// 尝试停止主机
        /// </summary>
        Task StopAsync(CancellationToken cancellationToken = default(CancellationToken));
    }


    internal class WebHost : IWebHost
    {
        private readonly IServiceCollection _applicationServiceCollection;
        private readonly IServiceProvider _hostingServiceProvider;
        private IServiceProvider _applicationServices;
        private RequestDelegate _application;
        private IStartup _startup;

        private IServer Server { get; set; }

        public WebHost(IServiceCollection appServices, IServiceProvider hostingServiceProvider)
        {
            _applicationServiceCollection = appServices;
            _hostingServiceProvider = hostingServiceProvider;
        }

        public WebHost()
        {
        }

        private void EnsureStartup()
        {
            if (_startup != null)
            {
                return;
            }

            _startup = _hostingServiceProvider.GetRequiredService<IStartup>();
        }

        public void Dispose()
        { }

        public IServiceProvider Services
        {
            get
            {
                if (_applicationServices == null)
                {
                    EnsureStartup();
                    _applicationServices = _applicationServiceCollection.BuildServiceProvider();
                }
                return _applicationServices;
            }
        }

        public IFeatureCollection ServerFeatures
        {
            get
            {
                return Server?.Features;
            }
        }

        public void Start()
        {
            StartAsync().GetAwaiter().GetResult();
        }

        public void Initialize()
        {
            if (_application == null)
            {
                _application = BuildApplication();
            }
        }
        private void EnsureServer()
        {
            if (Server == null)
            {
                Server = Services.GetRequiredService<IServer>();
            }
        }
        private RequestDelegate BuildApplication()
        {
            EnsureServer();

            var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>();
            var builder = builderFactory.CreateBuilder(Server.Features);
            builder.ApplicationServices = _applicationServices;
            Action<IApplicationBuilder> configure = _startup.Configure;

            configure(builder);

            return builder.Build();
        }


        public async Task StartAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            var hostingApp = new HostingApplication(_application);
            await Server.StartAsync(hostingApp, cancellationToken).ConfigureAwait(false);
        }

        public async Task StopAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            if (Server != null)
            {
                await Server.StopAsync(cancellationToken).ConfigureAwait(false);
            }

        }
    }

    public static class WebHostExtensions
    {
        public static void Run(this IWebHost host)
        {
            host.RunAsync().GetAwaiter().GetResult();
        }

        public static async Task RunAsync(this IWebHost host, CancellationToken token = default(CancellationToken))
        {
            // Wait for token shutdown if it can be canceled
            if (token.CanBeCanceled)
            {
                await host.RunAsync(token, shutdownMessage: null);
                return;
            }

            // If token cannot be canceled, attach Ctrl+C and SIGTERM shutdown
            var done = new ManualResetEventSlim(false);
            using (var cts = new CancellationTokenSource())
            {
                await host.RunAsync(cts.Token, "Application started. Press Ctrl+C to shut down.");
                done.Set();
            }
        }

        private static async Task RunAsync(this IWebHost host, CancellationToken token, string shutdownMessage)
        {
            using (host)
            {
                await host.StartAsync(token);
            }
        }

        private static async Task WaitForTokenShutdownAsync(this IWebHost host, CancellationToken token)
        {
            // WebHost will use its default ShutdownTimeout if none is specified.
            await host.StopAsync();
        }
    }

   
}